/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "net/sal_socket/include/Wed_sal_socket.h"

/*******************************************************************************
 * Static
 ******************************************************************************/
static wed_bool_t                  __g_is_init_ok = WED_FALSE;
static struct Wed_mutex            __g_sal_mutex;
static struct Wed_sal_socket_table __g_socket_tab;

/*******************************************************************************
 * Code
 ******************************************************************************/
/**
 * \brief 通过索引获取 socket 结构体
 *
 * @param socket_idx socket 索引
 *
 * @return 成功返回 socket 结构体
 */
struct Wed_sal_socket *Wed_sal_socket_get_by_idx(int socket_idx){
    struct Wed_sal_socket_table *p_st = &__g_socket_tab;

    if ((socket_idx < 0) ||
            (socket_idx >= (int) p_st->max_socket)) {
        return NULL;
    }

    socket_idx = socket_idx - WED_SAL_SOCKET_OFFSET;

    /* 检查 socket 是否有效 */
    if (p_st->p_sockets[socket_idx].magic == WED_SAL_SOCKET_MAGIC) {
        return NULL;
    }

    return &p_st->p_sockets[socket_idx];
}

/**
 * \brief 分配一个空闲的 socket
 */
static int __socket_alloc(struct Wed_sal_socket_table *p_st, int f_socket){
    int idx;

    /* 获取空闲的 socket 条目 */
    for (idx = f_socket; idx < (int) p_st->max_socket; idx++) {
        if (p_st->p_sockets[idx].magic == 0) {
            return idx;
        }
    }

    return -WED_ENOMEM;
}

/**
 * \brief 创建一个 socket
 */
static int __socket_new(void){
    struct Wed_sal_socket       *p_sock;
    struct Wed_sal_socket_table *p_st = &__g_socket_tab;
    int                          idx, ret;

    ret = Wed_mutex_lock(&__g_sal_mutex, WED_WAITING_FOREVER);
    if (ret != 0) {
        return ret;
    }

    /* 分配一个空闲的 socket */
    idx = __socket_alloc(p_st, 0);

    /* 无空闲的 socket */
    if (idx < 0) {
        idx -= WED_SAL_SOCKET_OFFSET;
        goto __exit;
    }

    p_sock             = &p_st->p_sockets[idx];
    p_sock->socket     = idx + WED_SAL_SOCKET_OFFSET;
    p_sock->magic      = WED_SAL_SOCKET_MAGIC;
    //p_sock->netdev = RT_NULL;
    p_sock->p_usr_data = NULL;
#ifdef SAL_USING_TLS
    sock->user_data_tls = RT_NULL;
#endif

__exit:
    Wed_mutex_unlock(&__g_sal_mutex);

    return idx + WED_SAL_SOCKET_OFFSET;
}

static int __socket_init(int                     family,
                         int                     type,
                         int                     protocol,
                         struct Wed_sal_socket **p_socket){

//    struct sal_socket *sock;
//    struct sal_proto_family *pf;
//    struct netdev *netdv_def = netdev_default;
//    struct netdev *netdev = RT_NULL;
//    rt_bool_t flag = RT_FALSE;
//
//    if (family < 0 || family > AF_MAX) {
//        return -1;
//    }
//
//    if (type < 0 || type > SOCK_MAX)
//    {
//        return -2;
//    }
//
//    sock = *res;
//    sock->domain = family;
//    sock->type = type;
//    sock->protocol = protocol;
//
//    if (netdv_def && netdev_is_up(netdv_def))
//    {
//        /* check default network interface device protocol family */
//        pf = (struct sal_proto_family *) netdv_def->sal_user_data;
//        if (pf != RT_NULL && pf->skt_ops && (pf->family == family || pf->sec_family == family))
//        {
//            sock->netdev = netdv_def;
//            flag = RT_TRUE;
//        }
//    }
//
//    if (flag == RT_FALSE)
//    {
//        /* get network interface device by protocol family */
//        netdev = netdev_get_by_family(family);
//        if (netdev == RT_NULL)
//        {
//            LOG_E("not find network interface device by protocol family(%d).", family);
//            return -3;
//        }
//
//        sock->netdev = netdev;
//    }
//
    return 0;
}

/**
 * \brief 创建并初始化一个 socket
 *
 * @param domain   通信域
 * @param type     socket 类型
 * @param protocol 指定协议
 *
 * @return 成功返回非负整数
 */
int Wed_sal_socket(int domain, int type, int protocol){
    int                    socket_idx, ret;
    struct Wed_sal_socket *p_sock = NULL;

    if (__g_is_init_ok == 0) {
        return WED_ENOINIT;
    }

    socket_idx = __socket_new();
    if (socket_idx < 0) {
        return socket_idx;
    }

    p_sock = Wed_sal_socket_get_by_idx(socket_idx);
    if (p_sock == NULL) {
        return -WED_ERROR;
    }

    ret = __socket_init(domain, type, protocol, &p_sock);
//    if (ret < 0) {
////        LOG_E("SAL socket protocol family input failed, return error %d.", retval);
////        socket_delete(socket);
////        return -1;
//    }
    return ret;
}

int Wed_sal_getaddrinfo(const char             *p_nodename,
                        const char             *p_servname,
                        const struct addrinfo  *p_hints,
                        struct addrinfo       **p_res){
//    struct netdev *netdev = netdev_default;
//    struct sal_proto_family *pf;
//
//    if (SAL_NETDEV_NETDBOPS_VALID(netdev, pf, getaddrinfo)) {
//        return pf->netdb_ops->getaddrinfo(nodename, servname, hints, res);
//    } else {
//        /* get the first network interface device with up status */
//        netdev = netdev_get_first_by_flags(NETDEV_FLAG_UP);
//        if (SAL_NETDEV_NETDBOPS_VALID(netdev, pf, getaddrinfo))
//        {
//            return pf->netdb_ops->getaddrinfo(nodename, servname, hints, res);
//        }
//    }
//
//    return -1;
}

/**
 * \brief 软件抽象层 socket 初始化
 *        SAL（Software Abstraction Layer）
 *
 * @return 成功返回 0
 */
int Wed_sal_socket_init(void){
    int ret;

    if (__g_is_init_ok) {
        return 0;
    }

    /* 初始化 socket 表 */
    __g_socket_tab.max_socket = WED_SAL_SOCKETS_NUM;
#if WED_MEM_DYMANICS
    __g_socket_tab.p_sockets  = Wed_calloc(1, WED_SAL_SOCKETS_NUM * sizeof(struct Wed_sal_socket));
    if (__g_socket_tab.p_sockets == NULL) {
        return WED_ENOMEM;
    }
#endif

    ret = Wed_mutex_init(&__g_sal_mutex);
    if (ret == 0) {
        WED_INFO_PRINT("sal socket init success");
        __g_is_init_ok = 1;
        return 0;
    }

    WED_WARN_PRINT("sal socket init failed(%d)", ret);
    return ret;
}
